use bevy::audio::PlaybackMode;
use bevy::prelude::*;
use bevy::window::{MonitorSelection, WindowMode};

#[bevy_main]
pub fn main() {
    init_logging();

    App::new()
        .add_plugins((DefaultPlugins.set(WindowPlugin {
            primary_window: Some(Window {
                name: Some("{{app.stylized-name}}".to_string()),
                resizable: false,
                mode: WindowMode::BorderlessFullscreen(MonitorSelection::Primary),
                ..default()
            }),
            ..default()
        }),))
        .init_resource::<Actions>()
        .add_systems(Startup, (setup, setup_audio, setup_player))
        .add_systems(Update, (handle_touch, move_player))
        .run();
}

#[cfg(target_os = "android")]
fn init_logging() {
    android_logger::init_once(
        android_logger::Config::default()
            .with_max_level(log::LevelFilter::Trace)
            .with_tag("hello-bevy"),
    );
}

#[cfg(not(target_os = "android"))]
fn init_logging() {
    env_logger::init();
}

#[unsafe(no_mangle)]
#[inline(never)]
#[cfg(target_os = "ios")]
pub extern "C" fn start_app() {
    main();
}

#[derive(Component)]
pub struct Player;

fn setup(mut commands: Commands) {
    commands.spawn(Camera2d::default());
}

#[derive(Component)]
struct MoveSound;

fn setup_audio(mut commands: Commands, asset_server: Res<AssetServer>) {
    // Create sound resource
    let sound = asset_server.load("audio/flying.ogg");
    commands.spawn((
        AudioPlayer::new(sound),
        MoveSound,
        PlaybackSettings {
            mode: PlaybackMode::Loop,
            paused: true,
            ..default()
        },
    ));
}

fn setup_player(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands
        .spawn((
            Sprite {
                image: asset_server.load("textures/bevy.png"),
                ..Default::default()
            },
            Transform::from_translation(Vec3::new(0., 0., 1.)),
        ))
        .insert(Player);
}

#[derive(Default, Resource)]
pub struct Actions {
    pub player_movement: Option<Vec2>,
}

pub enum GameControl {
    Up,
    Down,
    Left,
    Right,
}

impl GameControl {
    pub fn pressed(&self, keyboard_input: &Res<ButtonInput<KeyCode>>) -> bool {
        match self {
            GameControl::Up => {
                keyboard_input.pressed(KeyCode::KeyW) || keyboard_input.pressed(KeyCode::ArrowUp)
            }
            GameControl::Down => {
                keyboard_input.pressed(KeyCode::KeyS) || keyboard_input.pressed(KeyCode::ArrowDown)
            }
            GameControl::Left => {
                keyboard_input.pressed(KeyCode::KeyA) || keyboard_input.pressed(KeyCode::ArrowLeft)
            }
            GameControl::Right => {
                keyboard_input.pressed(KeyCode::KeyD) || keyboard_input.pressed(KeyCode::ArrowRight)
            }
        }
    }
}

pub fn get_movement(control: GameControl, input: &Res<ButtonInput<KeyCode>>) -> f32 {
    if control.pressed(input) { 1.0 } else { 0.0 }
}

pub const FOLLOW_EPSILON: f32 = 5.;

pub fn handle_touch(
    mut actions: ResMut<Actions>,
    keyboard_input: Res<ButtonInput<KeyCode>>,
    touch_input: Res<Touches>,
    player: Query<&Transform, With<Player>>,
    camera: Query<(&Camera, &GlobalTransform), With<Camera2d>>,
) -> Result {
    let mut player_movement = Vec2::new(
        get_movement(GameControl::Right, &keyboard_input)
            - get_movement(GameControl::Left, &keyboard_input),
        get_movement(GameControl::Up, &keyboard_input)
            - get_movement(GameControl::Down, &keyboard_input),
    );

    if let Some(touch_position) = touch_input.first_pressed_position() {
        let (camera, camera_transform) = camera.single()?;
        if let Some(touch_position) = camera
            .viewport_to_world_2d(camera_transform, touch_position)
            .ok()
        {
            let diff = touch_position - player.single()?.translation.xy();
            if diff.length() > FOLLOW_EPSILON {
                player_movement = diff.normalize();
            }
        }
    }

    if player_movement != Vec2::ZERO {
        actions.player_movement = Some(player_movement.normalize());
    } else {
        actions.player_movement = None;
    }

    Ok(())
}

fn move_player(
    time: Res<Time>,
    actions: Res<Actions>,
    mut player_query: Query<&mut Transform, With<Player>>,
    audio_control: Query<&AudioSink, With<MoveSound>>,
) {
    if actions.player_movement.is_none() {
        if let Ok(audio_control) = audio_control.single() {
            if !audio_control.is_paused() {
                audio_control.pause();
            }
        }
        return;
    }
    let speed = 150.;
    let movement = Vec3::new(
        actions.player_movement.unwrap().x * speed * time.delta_secs(),
        actions.player_movement.unwrap().y * speed * time.delta_secs(),
        0.,
    );
    for mut player_transform in &mut player_query {
        player_transform.translation += movement;
    }
    if let Ok(audio_control) = audio_control.single() {
        if audio_control.is_paused() {
            audio_control.play();
        }
    }
}
